import os

from math import sqrt
import time
import datetime as time_datetime

from pandas import read_csv
from pandas import DataFrame
from pandas import concat
from pandas import Series
from pandas import datetime

import numpy as np

from keras import optimizers
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from keras.callbacks import EarlyStopping
from keras.layers import Bidirectional
from keras.utils import multi_gpu_model

from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

import matplotlib.pyplot as plt

# 本包下所需要用到的工具 尽量通用 扩展到全局

def parse(x):
    return datetime.strptime(x, '%Y %m')

# 将数据切分为训练X和预测y（多步监督）
def series_to_supervised(sequence, n_steps):
    '''
    data to supervised learning data
    :param sequence: sequence data
    :param n_steps: steps
    :return: X[], y[]
    '''
    X, y = list(), list()
    for i in range(len(sequence)):
        # 寻找n步后的数据
        end_ix = i + n_steps
        # 检查是否还在序列内
        if end_ix > len(sequence) - 1:
            break
        # 划分X和y
        seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
        X.append(seq_x)
        y.append(seq_y)
    return np.array(X), np.array(y)


def scale(train, test):
    '''
    normalization data to [-1, 1]
    :param train:
    :param test:
    :return:
    '''
    # 归一范围->[-1,1]
    # 不 astype可能会出现1.0000000002这种情况
    train = train.astype('float32')
    test = test.astype('float32')
    scaler = MinMaxScaler(feature_range=(-1, 1))
    scaler = scaler.fit(train)
    # transform train
    train = train.reshape(train.shape[0], train.shape[1])
    train_scaled = scaler.transform(train)
    # transform test
    test = test.reshape(test.shape[0], test.shape[1])
    test_scaled = scaler.transform(test)
    return scaler, train_scaled, test_scaled


def invert_scale(scaler, yhat):
    ''' inverse normalization
    inverse
    :param scaler:
    :param yhat:
    :return:
    '''
    return scaler.inverse_transform(yhat)


def data_perpare(series_data, steps ):
    '''
    data prepare  数据准备
    :param series_data: 时间序列数据
    :param steps: 步长，单位为一个数据，例：十年步长，一个数据为每月均值(10*12)
    :return: normalized list:  X_train, y_train, X_test, y_test
    '''

    # 转化为列表
    series_value = series_data.values.tolist()
    series_list = []
    for value in series_value:
        series_list.append(value[0])

    # 数据准备（不带归一化会出现nan）
    # steps = 10*12 (10年数据，每个数据为一个月均值)
    X, y = series_to_supervised(series_list, steps)
    # 再留出12个月的数据作为测试
    X_train, X_test = X[:-12], X[-12:]
    y_train, y_test = y[:-12], y[-12:]
    # 拼接训练和测试数据->归一化
    y_train = y_train.reshape((y_train.shape[0],1))
    train = np.concatenate((X_train, y_train), axis=1)  # 把训练数据和结果拼接起来，形成矩阵，用于归一化
    y_test = y_test.reshape((y_test.shape[0],1))
    test = np.concatenate((X_test, y_test), axis=1)
    scaler, X, y = scale(train, test)
    # 再留出12个月的数据作为测试
    X_train,y_train = X[:, :-1], X[:, -1]
    X_test, y_test = y[:, :-1], y[:, -1]

    # 重塑[samples, timesteps]=>[samples, timesteps, features]
    n_features = 1
    X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], n_features))

    return X_train, y_train, X_test, y_test


import keras.backend as K

def get_activations(model, inputs, print_shape_only=False, layer_name=None):
    # Documentation is available online on Github at the address below.
    # From: https://github.com/philipperemy/keras-visualize-activations
    print('----- activations -----')
    activations = []
    inp = model.input
    if layer_name is None:
        outputs = [layer.output for layer in model.layers]
    else:
        outputs = [layer.output for layer in model.layers if layer.name == layer_name]  # all layer outputs
    funcs = [K.function([inp] + [K.learning_phase()], [out]) for out in outputs]  # evaluation functions
    layer_outputs = [func([inputs, 1.])[0] for func in funcs]
    for layer_activations in layer_outputs:
        activations.append(layer_activations)
        if print_shape_only:
            print(layer_activations.shape)
        else:
            print('activations shape:',layer_activations.shape)
            print(layer_activations)
    return activations